package com.ms.game.itembox;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ms.game.itembox.container.ItemBasic;
import com.ms.game.itembox.container.ItemBoxBin;
import com.ms.game.itembox.container.ItemGoldCoinCard;
import com.ms.game.itembox.container.ItemTreasureBox;
import com.ms.game.itembox.message.SBatchChangeGoldCoinCardResponse;
import com.ms.game.itembox.message.SSimpleItem;
import com.ms.player.Player;
import com.ms.player.PlayerUserType;
import com.ms.service.error.ErrorCode;
import com.ms.service.module.analysisgroup.AnalysisGroupProcessor;
import com.ms.service.module.zone.BasicZone;
import com.ms.service.module.zone.BlockType;
import com.ms.service.module.zone.module.BasicZoneModule;
import com.xpec.c4.game.common.Helper;
import com.xpec.c4.game.datamanager.BaseDataInfo;
import com.xpec.c4.game.datamanager.agent.money.ChangeCoin;
import com.xpec.c4.game.datamanager.agent.money.ChangeCoinLister;
import com.xpec.c4.game.datamanager.agent.money.CoinType;
import com.xpec.c4.game.datamanager.agent.money.MoneyChangeParam;
import com.xpec.c4.game.datamanager.agent.money.MoneyChangeWay;
import com.xpec.c4.game.datamanager.agent.moneyItem.MoneyItemChangeParam;
import com.xpec.c4.game.utility.GUID;
import com.xpec.c4.game.utility.GUIDFactory;
import com.xpec.c4.game.utility.GUIDType;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.message.ConstantCode;
import com.xpec.c4.message.MessageTypes;
import com.xpec.c4.service.contexts.Contexts;
import com.xpec.c4.service.message.MessageAction;
import com.xpec.c4.service.module.common.ItemInfoManager;
import com.xpec.c4.service.module.common.PlayerCommonInfoManager;

/**
 * 道具箱
 * 
 * @author TW15010
 */
public class ItemBox implements Serializable {
	private static final Log log = LogFactory.getLog(ItemBox.class);

	private static final long serialVersionUID = 4172461255723999690L;

	/** 找不到指定的道具欄位 */
	public static final int ITEM_NOT_FOUND = -1;
	
	/** C幣卡 defaultId */
	private final static String GOLD_COIN_ID = "GOLD_COIN";
	
	/** 批次處理C幣卡數量*/
	private final static int BATCH_CHANGE_GOLD_COIN_CARD_COUNT = 100;

	/**
	 * 道具欄位列表
	 */
	private ArrayList<Item> items = new ArrayList<Item>();

	/**
	 * 取得指定道具所在位置的 index
	 * 
	 * @param itemSn
	 *            道具序號
	 * @return 0 base index, 找不到傳回 {@link #ITEM_NOT_FOUND} (
	 *         {@value #ITEM_NOT_FOUND})
	 */
	private int getItemIndex(String itemSn) {
		if (!GameUtility.isHaveString(itemSn)) {
			return ITEM_NOT_FOUND;
		}

		int imax = items.size();
		for (int i = 0; i < imax; i++) {
			Item item = items.get(i);
			if (item.getSn().equals(itemSn)) {
				return i;
			}
		}

		return ITEM_NOT_FOUND;
	}

	/**
	 * 取得指定道具欄位
	 * 
	 * @param itemSn
	 *            道具序號
	 * @return 成功: 道具資料 {@link Item}<br>
	 *         失敗: null
	 */
	public Item getItem(String itemSn) {
		int idx = getItemIndex(itemSn);
		if (idx != ITEM_NOT_FOUND) {
			Item item = items.get(idx);
			return item;
		}

		return null;
	}

	/**
	 * 取得指定序號道具的數量
	 * 
	 * @param itemSn
	 *            道具序號
	 * @return 數量, 0 表示物品不存在
	 */
	public int getItemCount(String itemSn) {
		Item item = getItem(itemSn);
		if (item == null) {
			return 0;
		}

		return item.getCount();
	}

	/**
	 * 用 itemId 取得指定道具數量
	 * 
	 * @param itemId
	 *            道具 id {@link ItemBoxBin}
	 * @return 道具數量
	 */
	public int getItemCountById(String itemId) {
		for (Item item : items) {
			if (item.getItemId().equals(itemId)) {
				return item.getCount();
			}
		}

		return 0;
	}

	/**
	 * 設定道具數量
	 * 
	 * @param itemSn
	 *            道具序號
	 * @param count
	 *            道具數量，如果道具數量小於等於 0 就會將道具欄位刪除
	 */
	public Item setItemCount(String itemSn, int count) {
		Item item = getItem(itemSn);
		if (item == null) {
			return null;
		}

		if (count <= 0) {
			item.setCount(0);
			removeItem(itemSn);
			return item;
		}

		item.setCount(count);
		return item;
	}

	/**
	 * 新增道具到道具箱中
	 * 
	 * @param newItem
	 *            新道具資料
	 */
	public void addItem(Item newItem) {
		if (newItem == null) {
			return;
		}

		int itemIndex = getItemIndex(newItem.getSn());

		// 道具不存在現有資料中
		if (itemIndex == ITEM_NOT_FOUND) {
			items.add(newItem);
			return;
		}

		// 道具已存在資料中，覆蓋進去
		items.set(itemIndex, newItem);
	}

	/**
	 * 刪除指定的物品欄位
	 * 
	 * @param itemSn
	 *            物品序號
	 */
	public void removeItem(String itemSn) {
		int idx = getItemIndex(itemSn);
		if (idx != ITEM_NOT_FOUND) {
			items.remove(idx);
		}
	}

	/**
	 * 使用物品
	 * 
	 * @param curModule
	 *            目前所在 module
	 * @param gameHallId
	 *            遊戲館id，沒有就給空字串
	 * @param gameHallName
	 *            遊戲館名稱，沒有就給空字串
	 * @param player
	 *            角色資料
	 * @param itemSn
	 *            物品序號
	 * @param useCount
	 *            使用數量
	 * @param targetName
	 *            目標 player name，沒有目標就給 null
	 * @return 錯誤代碼 {@link ErrorCode}
	 */
	public int useItem(BasicZoneModule curModule, String gameHallId, String gameHallName, Player player,
			String itemSn, int useCount, String targetName) {
		// 目前所在 module
		if (curModule == null) {
			return ErrorCode.INVALID_PARAM;
		}

		// 遊戲館 id
		if (gameHallId == null) {
			return ErrorCode.INVALID_PARAM;
		}

		// 遊戲館名稱
		if (gameHallName == null) {
			return ErrorCode.INVALID_PARAM;
		}

		// 驗證道具序號
		if (!GameUtility.isHaveString(itemSn)) {
			return ErrorCode.INVALID_PARAM;
		}

		// 從角色身上取得指定的物品
		Item item = getItem(itemSn);
		if (item == null) {
			return ErrorCode.INVALID_PARAM;
		}

		// 道具不存在於數據中
		ItemBasic itemBasic = ItemInfoManager.getInstance().getItem(item.getItemId());
		if (itemBasic == null) {
			return ErrorCode.USE_ITEM_ITEM_NOT_FOUND_FROM_BIN;
		}
		
		if(!itemBasic.isOpen()) { //尚未開放
			return ErrorCode.ITEM_NOT_OPEN_PLEASE_WAIT;
		}
			
		if (!itemBasic.isLevelEnoughToUse(player.getLevel())) { // 判斷等級是否能使用
			return ErrorCode.PLAYER_LEVEL_NOT_ENOUGH;
		}
		
		if(itemBasic.isTreasureBox()) { //如果是循環寶箱要另外看
			if(((ItemTreasureBox)itemBasic).getTreasureBoxType() == ItemTreasureBox.TYPE_RECYCLE){ 
				if(player.getLevel() < item.getExtraSetting().getUseLevel()){
					return ErrorCode.PLAYER_LEVEL_NOT_ENOUGH;
				}
				
				if(player.getLastLevelBoxUseLevel() >= item.getExtraSetting().getUseLevel()){//上次開過的寶箱等級跟這次不會一樣
					return ErrorCode.ITEM_LEVEL_BOX_USE_LEVEL_ERROR;
				}
			}
		}
		
		if (!itemBasic.isVipEnoughToUse(player.getVip())) { // 判斷vip等級是否能使用
			return ErrorCode.ITEM_USE_VIP_LEVEL_NOT_ENOUGH;
		}

		// 需要指定目標
		GUID targetGuid = null;
		if (itemBasic.isNeedTarget()) {
			// 目標
			if (targetName == null) {
				return ErrorCode.USE_ITEM_NEED_TARGET;
			}

			// 是否在線上
			targetGuid = PlayerCommonInfoManager.getInstance().getGuidByPlayerName(targetName);
			if (targetGuid == null) {
				return ErrorCode.USE_ITEM_TARGET_IS_NOT_ONLINE;
			}
		}

		// 身上是否有指定物品
		int itemCount = getItemCount(itemSn);
		if (itemCount == 0) {
			return ErrorCode.USE_ITEM_ITEM_NOT_FOUND_FROM_PLAYER;
		}
		
		// order id
		GUID orderId = GUIDFactory.createUUID(GUIDType.TYPE_USE_ITEM);

		// change item lister
		ChangeItemLister itemLister = new ChangeItemLister();
		if (!itemLister.addItem(itemSn, null, useCount, item.getRefineLevel())) {
			return ErrorCode.USE_ITEM_INVALID_USE_COUNT;
		}

		// 先扣除物品
		boolean result = curModule.getPlayerDataAgent().updatePlayerItemToDC(orderId, player, targetGuid,
				itemLister, ItemChangeWay.SUB_USE_ITEM, gameHallId, gameHallName, null, ItemChangeParam.NO_EXP, this, "updateItemToDcCallback",
				player, targetGuid, item, orderId);

		return (result ? ErrorCode.SUCCESS : ErrorCode.USE_ITEM_CONSUME_ITEM_PRE_CHECK_FAIL);
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		int cnt = 0;
		for (Item item : items) {
			if (cnt > 0) {
				sb.append("\n");
			}

			// 取得物品名稱
			ItemBasic itemBasic = ItemInfoManager.getInstance().getItem(item.getItemId());
			String itemName;
			if (itemBasic != null) {
				itemName = itemBasic.getName();
			} else {
				itemName = "item not exist in bin file";
			}

			// [0] sn:...<sn> <id> <itemName> cnt:3 lv:1
			sb.append("[" + cnt + "] sn:" + GameUtility.rightOfString(item.getSn(), 5) + " " + item.getItemId()
					+ " " + itemName + " cnt:" + item.getCount() + " lv:" + item.getRefineLevel());
			cnt++;
		}

		return sb.toString();
	}

	/**
	 * 使用道具前
	 * {@link #useItem(BasicZoneModule, String, String, Player, String, int, GUID)
	 * useItem()} 先向 dc 要求扣除物品的 callback
	 * 
	 * @param obj
	 * @param userObject
	 *            [0] Player player, [1] GUID targetGuid
	 */
	public void updateItemToDcCallback(BaseDataInfo obj, Object... userObject) {
		ItemChangeParam param = (ItemChangeParam) obj.getData();
//		Player player = (Player) userObject[0];
		GUID targetGuid = (GUID) userObject[1];
		Item useItem = (Item) userObject[2];
		GUID orderId = (GUID)userObject[3];

		// 道具一次只能使用一種，所以只要取第一個出來使用就可以了
		ChangeItemLister itemLister = param.getChangeItemLister();
		Iterator<Map.Entry<String, ChangeItem>> it = itemLister.getListIterator();
		ChangeItem changeItem = itemLister.getNextItem(it);

		BasicZoneModule zoneModule = (BasicZoneModule)Contexts.getModule();
		BasicZone basicZone = zoneModule.getZone();
		Player player = basicZone.getPlayer(param.getPlayerId());
		if (player == null) { //角色已經不在線上了
			return;
		}
		
		if (param.getResult() == ErrorCode.SUCCESS) {
			if (changeItem != null) {
				// 找到物品
				ItemBasic item = ItemInfoManager.getInstance().getItem(changeItem.getItemId());
				if (item != null) {
					
					if (!item.isApplyItemToDc()) { //使用道具不需要經過DC
						// 使用
						int resultCode = item.use(player, useItem, changeItem.getChangeCount(), targetGuid);
						
						//移除block list
						basicZone.removeBlockList(player.getGuid(), null);
						// 失敗
						if (resultCode != ErrorCode.SUCCESS) {
							// 通知 client
							sendUseItemResponseToClient(player.getSessionID(), resultCode, player.getAccountId(), player.getGuid());

							// 寫 log
							if (log.isErrorEnabled()) {
								// player <player name> use item <item name> fail
								// (<error description>).
								log.error("playse " + player + " use item " + item.getName() + " fail ("
										+ ConstantCode.getName(ErrorCode.class, resultCode) + ").");
							}
						} else {
							sendUseItemResponseToClient(player.getSessionID(), resultCode, player.getAccountId(), player.getGuid());
						}
					}else { //使用道具需要經過DC再回來
						// 使用
						int resultCode = item.applyItemToDc(zoneModule, player, useItem, orderId);
						
						if (resultCode != ErrorCode.SUCCESS) {
							//移除block list
							basicZone.removeBlockList(player.getGuid(), null);
							
							// 通知 client
							sendUseItemResponseToClient(player.getSessionID(), resultCode, player.getAccountId(), player.getGuid());

							// 寫 log
							if (log.isErrorEnabled()) {
								// player <player name> use item <item name> fail
								// (<error description>).
								log.error("player " + player + " use item " + item.getName() + " fail ("
										+ ConstantCode.getName(ErrorCode.class, resultCode) + ").");
							}
						}
					}
				}
			} // if (changeItem != null)
		} else { // if (param.getResult() == ErrorCode.SUCCESS)
			
			//移除block list
			basicZone.removeBlockList(player.getGuid(), null);
			
			// 傳 error code 給 client
			int resultCode = param.getResult();

			// 是個別物品的錯誤
			if (resultCode == ErrorCode.CHANGE_ITEM_ITEM_ERROR) {
				resultCode = changeItem.getResult();
			}

			sendUseItemResponseToClient(player.getSessionID(), resultCode, player.getAccountId(), player.getGuid());

			// // 失敗寫 log
			if (log.isErrorEnabled()) {
				log.error(param.getResultString());
			}
		} // if (param.getResult() == ErrorCode.SUCCESS) else
	}

	/**
	 * 傳送使用道具的結果給 client
	 * 
	 * @param sessionId
	 *            session id
	 * @param resultCode
	 *            錯誤代碼 {@link ErrorCode}
	 */
	public static void sendUseItemResponseToClient
	(
			GUID sessionId,
			int resultCode,
			GUID accountID,
			GUID playerGUID
	)
	{
		
		MessageAction.sendMessage(sessionId, MessageTypes.S_ZONE_USE_ITEM_RESPONSE, resultCode);
	}

	public ArrayList<Item> getItems() {
		return items;
	}

	public void setItems(ArrayList<Item> items) {
		this.items = items;
	}
	
	/**
	 * 批次換C幣卡
	 */
	public void batchChangeGoldCoinCard(BasicZoneModule curModule, String gameHallId, String gameHallName, Player player) {
		GUID playerId = player.getGuid();
		GUID sessionId =  player.getSessionID();

		BasicZone basicZone = curModule.getZone();
		if (basicZone.isBlock(playerId, null)) {
			SBatchChangeGoldCoinCardResponse response = new SBatchChangeGoldCoinCardResponse();
			response.setErrorCode(ErrorCode.ITEM_CAN_NOT_BATCH_CHANGE_GOLD_COIN_CARD_NOW);
			MessageAction.sendMessage(sessionId, MessageTypes.S_ZONE_BATCH_CHANGE_GOLD_COIN_CARD_RESPONSE, response);
			return;
		}	
		
		basicZone.putBlockList(playerId, null, BlockType.USE_ITEM);
		
		int modifyCount = 0;
		boolean isFinish = true;
		ArrayList<Item> modifyItems = new ArrayList<Item>();
		for (Item item : items) {
			if(modifyCount >= 100) { //數量到了 不處理囉
				isFinish = false;
				break;
			}
			
			if (item.getBasicItemId().contains(GOLD_COIN_ID)) { // C幣卡
				modifyCount ++ ;
				modifyItems.add(item);
			}
		}

		if(modifyCount <= 0){
			SBatchChangeGoldCoinCardResponse response = new SBatchChangeGoldCoinCardResponse();
			response.setFinish(true);
			MessageAction.sendMessage(sessionId, MessageTypes.S_ZONE_BATCH_CHANGE_GOLD_COIN_CARD_RESPONSE, response);
			basicZone.removeBlockList(playerId);
			
			if (log.isErrorEnabled()) {
				StringBuilder builder = new StringBuilder();
				builder.append("[BatchChangeGoldCoinCard]");
				builder.append("[NO GOLD COIN CARD]");
				builder.append("PlayerId=");
				builder.append(playerId);			
				log.error(builder.toString());
			}
			return;
		}
		
		ChangeItemLister itemLister = new ChangeItemLister();
		
		BigDecimal goldCoinValue = BigDecimal.ZERO;
		for (Item item : modifyItems) {
			itemLister.addItem(item.getSn(), item.getItemId(), item.getCount(), item.getRefineLevel());
			ItemBasic itemBasic = ItemInfoManager.getInstance().getItem(item.getItemId());
			ItemGoldCoinCard goldCoinCard = (ItemGoldCoinCard)itemBasic;
			goldCoinValue = Helper.add(goldCoinValue, new BigDecimal(goldCoinCard.getGoldCoin()));
		}
		
		if (log.isInfoEnabled()) {
			StringBuilder builder = new StringBuilder();
			builder.append("[BatchChangeGoldCoinCard]");
			builder.append("PlayerId=");
			builder.append(playerId);
			builder.append("|ModifyCount=");
			builder.append(modifyCount);
			builder.append("|GoldCoinValue=");
			builder.append(goldCoinValue.longValue());
			log.info(builder.toString());
		}
		
		ChangeCoinLister coinLister = new ChangeCoinLister();
		coinLister.addCoin(CoinType.GOLD, goldCoinValue);
		
		GUID orderId = GUIDFactory.createUUID(GUIDType.TYPE_USE_ITEM);
		
		curModule.getPlayerDataAgent().updatePlayerMoneyAndItemToDC
		(
				orderId,
				player,
				coinLister,
				MoneyChangeWay.ADD_USE_GOLDCOIN_CARD,
				itemLister,
				ItemChangeWay.SUB_USE_ITEM,
				null,
				gameHallId,
				gameHallName,
				this,
				"batchUpdateGoldCoinCardToDcCallback",
				sessionId, //userObject[0]
				modifyItems, //userObject[1]
				playerId, //userObject[2]
				isFinish, //userObject[3]
				player.getUserType() //userObject[4]
		);
	
	}
	
	public void batchUpdateGoldCoinCardToDcCallback(BaseDataInfo obj, Object... userObject) {
		SBatchChangeGoldCoinCardResponse response = new SBatchChangeGoldCoinCardResponse();
		GUID sessionId = (GUID)userObject[0];
		GUID playerId = (GUID)userObject[2];
		boolean isFinish = (boolean)userObject[3];
		response.setFinish(isFinish);
		int userType = (int)userObject[4];
		
		if (obj.getResultCode() == ErrorCode.SUCCESS) {
			response.setErrorCode(ErrorCode.SUCCESS);
			
			MoneyItemChangeParam param = (MoneyItemChangeParam)obj.getData();
			MoneyChangeParam moneyChangeParam = param.getMoneyParam();
			ChangeCoinLister coinLister = moneyChangeParam.getChangeCoinLister();
			long goldCoin = 0;
			if(coinLister!= null){
				ChangeCoin coin = coinLister.getCoinMap().get(CoinType.GOLD);
				goldCoin = coin.getChangeValue().longValue();
			}
			
			ArrayList<SSimpleItem> simpleItems = new ArrayList<SSimpleItem>();
			ArrayList<Item> goldCoins = (ArrayList<Item>) userObject[1];
			
			HashMap<String, SSimpleItem> checkMap = new HashMap<String, SSimpleItem>();
			for (Item item : goldCoins) {
				SSimpleItem simpleItem = item.toClientFormat();
				if(!checkMap.containsKey(item.getItemId())){
					checkMap.put(item.getItemId(), simpleItem);
					simpleItems.add(simpleItem);
					continue;
				}
				
				simpleItems.add(checkMap.get(item.getItemId()));
			}
			
			response.setGoldCoins(simpleItems);
			response.setGoldCoinValue(goldCoin);
			
			MessageAction.sendMessage(sessionId, MessageTypes.S_ZONE_BATCH_CHANGE_GOLD_COIN_CARD_RESPONSE, response);
			
			if(userType == PlayerUserType.PLAYER){
				AnalysisGroupProcessor.sendBatchCoinCardDetailToAnalysis(goldCoins);
			}
			
		}else {
			
			response.setErrorCode(ErrorCode.DB_OPERATION_FAIL);
			MessageAction.sendMessage(sessionId, MessageTypes.S_ZONE_BATCH_CHANGE_GOLD_COIN_CARD_RESPONSE, response);
		}
		
		BasicZone basicZone = ((BasicZoneModule)Contexts.getModule()).getZone();
		basicZone.removeBlockList(playerId);
	}
	
}
